import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from ParzenWindowMethod import load_data

def distance(v1, v2):
    """
     计算欧式距离
     :param v1: array
     :param v2: array
     :return: dist
     """
    dist = np.sqrt(np.sum(np.power((v1 - v2), 2)))
    return dist


def KNN(train, test, k):
    acc_num = 0

    # 对每个待分类样本进行KNN分类
    for i in range(test.shape[0]):
        # 保存计算的距离与分类标签
        arr_dist = np.zeros(shape=(train.shape[0], 2))
        for j in range(train.shape[0]):
            dist = distance(test[i, :-1], train[j, :-1])
            arr_dist[j, :] = dist, train[j, -1]

        # 创建二维表，列名依次为'dist', 'target'，保存样本数据点与训练集所有点的欧氏距离，
        df = pd.DataFrame(data=arr_dist, columns=['dist', 'target'])  # DataFrame可以设置列名columns与行名index，字符串或数字皆可
        # 对类别根据距离进行升序排列，取前k个值对应的类别，取其第一个众数为最终分类结果。也就是前k个投票表决，决定分类结果
        # .sort_values:默认为True，即升序排列  .head:返回前n行。  .mode:返回众数  [0]只保留第一个众数
        mode = df.sort_values(by='dist')['target'].head(k).mode()[0]
        # 正确分类结果计数
        if mode == test[i, -1]:
            acc_num += 1

    accuracy = acc_num / test.shape[0]

    return accuracy


if __name__ == '__main__':
    # 读取数据
    train, test = load_data()

    k_list = []
    accuracy_list = []

    for k in range(1, train.shape[0] + 1):
        # k值应小于能投票的训练集数量120
        accuracy = KNN(train, test, k)
        accuracy_list.append(accuracy)
        k_list.append(k)

    plt.plot(k_list, accuracy_list)
    plt.show()
